#pragma once 

#include <queue>
#include <unistd.h>
#include <stdlib.h>
#include <iostream>
#include <pthread.h>
using namespace std;

template<class T>
class BlockQueue
{
  public:
    BlockQueue(size_t capcity = 5)
      :_capacity(capcity)
    {
      pthread_cond_init(&full, nullptr);
      pthread_cond_init(&empty, nullptr);
      pthread_mutex_init(&mutex, nullptr);
    }

    ~BlockQueue()
    {
      pthread_cond_destroy(&full);
      pthread_cond_destroy(&empty);
      pthread_mutex_destroy(&mutex);
    }

    void PushData(T data)
    {
      pthread_mutex_lock(&mutex);
      while(IsFull())
      {
        cout<<"queue is full"<<endl;
        pthread_cond_signal(&empty);
        pthread_cond_wait(&full, &mutex);
      }
      _q.push(data);
      pthread_mutex_unlock(&mutex);
    }

    void PopData(T& data)
    {
      pthread_mutex_lock(&mutex);
      while(IsEmpty())
      {
        cout<<"queue is empty"<<endl;
        pthread_cond_signal(&full);
        pthread_cond_wait(&empty, &mutex);
      }
      data = _q.front();
      _q.pop();
      pthread_mutex_unlock(&mutex);
    }

  private:
    bool IsFull()
    {
      return _q.size() >= _capacity;
    }

    bool IsEmpty()
    {
      return _q.empty();
    }

    queue<T> _q;
    size_t _capacity;
    pthread_cond_t full;
    pthread_cond_t empty;
    pthread_mutex_t mutex;
};
